// ******************************************************************************************************
// <copyright file="Complex.cs" company="Caroso.inc">
//     Copyright (c) Carlos Osorio All rights reserved.
// </copyright>
// ******************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http:// www.mozilla.org/MPL/ 
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
// 
// The Original Code is this.mapWindow 
// 
// The Initial Developer of this version of the Original Code is hanzzoid@gmx.net
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
// Change Log: 
// Date           Changed By      Notes
// 22 Nov 2010    Carlos Osorio   This is an adaptation of C# Matrix library by hanzzoid@gmx.net July 2, 2007
// ******************************************************************************************************
namespace GeostatisticalTool.Classes
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    /// <summary>
    /// Class auxiliar to manage complex numbers
    /// </summary>
    public class Complex
    {
        /// <summary>
        /// Real parte of the number
        /// </summary>
        private double realpart;

        /// <summary>
        /// Imaginary part
        /// </summary>
        private double imaginaryPart;

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Complex class number as (0, 0).
        /// </summary>
        public Complex()
        {
            this.realpart = 0;
            this.imaginaryPart = 0;
        }

        /// <summary>
        /// Initializes a new instance of the Complex class number with imaginary part = 0.
        /// </summary>
        /// <param name="real_part">Real part of the complex number</param>
        public Complex(double real_part)
        {
            this.realpart = real_part;
            this.imaginaryPart = 0;
        }

        /// <summary>
        /// Initializes a new instance of the Complex class number.
        /// </summary>
        /// <param name="real_part">Real part of the complex number</param>
        /// <param name="imaginary_part">Imaginary part</param>
        public Complex(double real_part, double imaginary_part)
        {
            this.realpart = real_part;
            this.imaginaryPart = imaginary_part;
        }

        /// <summary>
        /// Initializes a new instance of the Complex class number from string like "a+bi".
        /// </summary>
        /// <param name="s">text of complex</param>
        public Complex(string s)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region fields

        /// <summary>
        /// Gets Complex number zero.
        /// </summary>
        public static Complex Zero
        {
            get
            {
                return new Complex(0, 0);
            }
        }

        /// <summary>
        /// Gets Complex number one.
        /// </summary>
        public static Complex One
        {
            get
            {
                return new Complex(1, 0);
            }
        }

        /// <summary>
        /// Gets Imaginary unit.
        /// </summary>
        public static Complex I
        {
            get
            {
                return new Complex(0, 1);
            }
        }

        /// <summary>
        /// Gets or sets real number
        /// </summary>
        public double Realnumber
        {
            get { return this.realpart; }
            set { this.realpart = value; }
        }

        /// <summary>
        /// Gets or sets contains the imaginary part of a complex number.
        /// </summary>
        public double Imaginarynumber
        {
            get { return this.imaginaryPart; }
            set { this.imaginaryPart = value; }
        }

        #endregion

        #region Operators

        /// <summary>
        /// Operation addition complex numbers
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator +(Complex a, Complex b)
        {
                return new Complex(a.realpart + b.realpart, a.imaginaryPart + b.imaginaryPart);
        }

        /// <summary>
        /// Operation addition complex number with real number
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second real number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator +(Complex a, double b)
        {
            return new Complex(a.realpart + b, a.imaginaryPart);
        }

        /// <summary>
        /// Operation addition real number with complex number
        /// </summary>
        /// <param name="a">First real number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator +(double a, Complex b)
        {
            return new Complex(a + b.realpart, b.imaginaryPart);
        }

        /// <summary>
        /// Operation substraction complex numbers
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator -(Complex a, Complex b)
        {
            return new Complex(a.realpart - b.realpart, a.imaginaryPart - b.imaginaryPart);
        }

        /// <summary>
        /// Operation substraction complex number with a real number
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second real number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator -(Complex a, double b)
        {
            return new Complex(a.realpart - b, a.imaginaryPart);
        }

        /// <summary>
        /// Operation substraction complex number with a real number
        /// </summary>
        /// <param name="a">First real number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator -(double a, Complex b)
        {
            return new Complex(a - b.realpart, -b.imaginaryPart);
        }

        /// <summary>
        /// Operation substraction complex number 
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator -(Complex a)
        {
            return new Complex(-a.realpart, -a.imaginaryPart);
        }

        /// <summary>
        /// Operation multiplication between complex numbers
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator *(Complex a, Complex b)
        {
            return new Complex((a.realpart * b.realpart) - (a.imaginaryPart * b.imaginaryPart), (a.imaginaryPart * b.realpart) + (a.realpart * b.imaginaryPart));
        }

        /// <summary>
        /// Operation multiplication complex number with a real number
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="d">Second real number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator *(Complex a, double d)
        {
            return new Complex(d) * a;
        }

        /// <summary>
        /// Operation multiplication real number with a complex number
        /// </summary>
        /// <param name="d">First real number</param>
        /// <param name="a">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator *(double d, Complex a)
        {
            return new Complex(d) * a;
        }

        /// <summary>
        /// Operation division complex number with a real number
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator /(Complex a, Complex b)
        {
            return a * Conj(b) * (1 / (Abs(b) * Abs(b)));
        }

        /// <summary>
        /// Operation division complex number with a real number
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Second real number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator /(Complex a, double b)
        {
            return a * (1 / b);
        }

        /// <summary>
        /// Operation division real number with a complex number
        /// </summary>
        /// <param name="a">First real number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number result</returns>
        public static Complex operator /(double a, Complex b)
        {
            return a * Conj(b) * (1 / (Abs(b) * Abs(b)));
        }

        /// <summary>
        /// Comparation of numbers
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>False or true</returns>
        public static bool operator ==(Complex a, Complex b)
        {
            return a.realpart == b.realpart && a.imaginaryPart == b.imaginaryPart;
        }

        /// <summary>
        /// Comparation of numbers
        /// </summary>
        /// <param name="a">First complex number</param>
        /// <param name="b">Real number</param>
        /// <returns>False or true</returns>
        public static bool operator ==(Complex a, double b)
        {
            return a == new Complex(b);
        }

        /// <summary>
        /// operator equals
        /// </summary>
        /// <param name="a">real number</param>
        /// <param name="b">Complex number</param>
        /// <returns>False or true</returns>
        public static bool operator ==(double a, Complex b)
        {
            return new Complex(a) == b;
        }

        /// <summary>
        /// Operator difference
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <param name="b">Complex number to be compared</param>
        /// <returns>False or true</returns>
        public static bool operator !=(Complex a, Complex b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Operator difference
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <param name="b">real number to be compared</param>
        /// <returns>False or true</returns>
        public static bool operator !=(Complex a, double b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Operator difference
        /// </summary>
        /// <param name="a">double number</param>
        /// <param name="b">Complex number to be compared</param>
        /// <returns>False or true</returns>
        public static bool operator !=(double a, Complex b)
        {
            return !(a == b);
        }

        #endregion

        #region Static funcs & overrides

        /// <summary>
        /// Calcs the absolute value of a complex number.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Real number</returns>
        public static double Abs(Complex a)
        {
            return Math.Sqrt((a.imaginaryPart * a.imaginaryPart) + (a.realpart * a.realpart));
        }

        /// <summary>
        /// Inverts a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex result</returns>
        public static Complex Inv(Complex a)
        {
            return new Complex(a.realpart / ((a.realpart * a.realpart) + (a.imaginaryPart * a.imaginaryPart)),  -a.imaginaryPart / ((a.realpart * a.realpart) + (a.imaginaryPart * a.imaginaryPart)));
        }

        /// <summary>
        /// Tangent of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex result</returns>
        public static Complex Tan(Complex a)
        {
            return Sin(a) / Cos(a);
        }

        /// <summary>
        /// Hyperbolic cosine of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>complex result</returns>
        public static Complex Cosh(Complex a)
        {
            return (Exp(a) + Exp(-a)) / 2;
        }

        /// <summary>
        /// Hyperbolic sine of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Return Complex number</returns>
        public static Complex Sinh(Complex a)
        {
            return (Exp(a) - Exp(-a)) / 2;
        }

        /// <summary>
        /// Hyperbolic tangent of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Return Complex number</returns>
        public static Complex Tanh(Complex a)
        {
            return (Exp(2 * a) - 1) / (Exp(2 * a) + 1);
        }

        /// <summary>
        /// Hyperbolic cotangent of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Coth(Complex a)
        {
            return (Exp(2 * a) + 1) / (Exp(2 * a) - 1);
        }

        /// <summary>
        /// Hyperbolic secant of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Sech(Complex a)
        {
            return Inv(Cosh(a));
        }

        /// <summary>
        /// Hyperbolic cosecant of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number returned</returns>
        public static Complex Csch(Complex a)
        {
            return Inv(Sinh(a));
        }

        /// <summary>
        /// Cotangent of a.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Cot(Complex a)
        {
            return Cos(a) / Sin(a);
        }

        /// <summary>
        /// Computes the conjugation of a complex number.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Conj(Complex a)
        {
            return new Complex(a.realpart, -a.imaginaryPart);
        }

        /// <summary>
        /// Complex square root.
        /// </summary>
        /// <param name="d">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Sqrt(double d)
        {
            if (d >= 0)
            {
                return new Complex(Math.Sqrt(d));
            }
            else
            {
                return new Complex(0, Math.Sqrt(-d));
            }
        }

        /// <summary>
        /// Complex square root.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>   
        public static Complex Sqrt(Complex a)
        {
            return Pow(a, .5);
        }

        /// <summary>
        /// Complex exponential function.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Exp(Complex a)
        {
            return new Complex(Math.Exp(a.realpart) * Math.Cos(a.imaginaryPart), Math.Exp(a.realpart) * Math.Sin(a.imaginaryPart));
        }

        /// <summary>
        /// Main value of the complex logarithm.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Log(Complex a)
        {
           //// Log[|w|]+I*(Arg[w]+2*Pi*k)            
            return new Complex(Math.Log(Abs(a)), Arg(a));
        }

        /// <summary>
        /// Argument of the complex number.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static double Arg(Complex a)
        {
            if (a.realpart < 0)
            {
                if (a.imaginaryPart < 0)
                {
                    return Math.Atan(a.imaginaryPart / a.realpart) - Math.PI;
                }
                else
                {
                    return Math.PI - Math.Atan(-a.imaginaryPart / a.realpart);
                }
            }
            else
            {
                return Math.Atan(a.imaginaryPart / a.realpart);
            }
        }

        /// <summary>
        /// Complex cosine.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Cos(Complex a)
        {
            return .5 * (Exp(Complex.I * a) + Exp(-Complex.I * a));
        }

        /// <summary>
        /// Complex sine.
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Sin(Complex a)
        {
            return (Exp(Complex.I * a) - Exp(-Complex.I * a)) / (2 * Complex.I);
        }

        /// <summary>
        /// Complex Pow operator
        /// </summary>
        /// <param name="a">First Complex number</param>
        /// <param name="b">Second complex number</param>
        /// <returns>Complex number return</returns>
        public static Complex Pow(Complex a, Complex b)
        {
            return Exp(b * Log(a));
        }

        /// <summary>
        /// Complex Pow
        /// </summary>
        /// <param name="a">Real value</param>
        /// <param name="b">Complex value</param>
        /// <returns>Complex number return</returns>
        public static Complex Pow(double a, Complex b)
        {
            return Exp(b * Math.Log(a));
        }

        /// <summary>
        /// Operator Pow 
        /// </summary>
        /// <param name="a">Complex number</param>
        /// <param name="b">Real value</param>
        /// <returns>Complex number return</returns>
        public static Complex Pow(Complex a, double b)
        {
            return Exp(b * Log(a));
        }

        /// <summary>
        /// Convert to string
        /// </summary>
        /// <returns>return string</returns>
        public override string ToString()
        {
            if (this == Complex.Zero)
            {
                return "0";
            }

            string re, im, sign;

            if (this.imaginaryPart < 0)
            {
                if (this.realpart == 0)
                {
                    sign = "-";
                }
                else
                {
                    sign = " - ";
                }
            }
            else if (this.imaginaryPart > 0 && this.realpart != 0)
            {
                sign = " + ";
            }
            else
            {
                sign = string.Empty;
            }

            if (this.realpart == 0)
            {
                re = string.Empty;
            }
            else
            {
                re = this.realpart.ToString();
            }

            if (this.imaginaryPart == 0)
            {
                im = string.Empty;
            }
            else if (this.imaginaryPart == -1 || this.imaginaryPart == 1)
            {
                im = "i";
            }
            else
            {
                im = Math.Abs(this.imaginaryPart).ToString() + "i";
            }

            return re + sign + im;
        }

        /// <summary>
        /// Convert to string
        /// </summary>
        /// <param name="format">Format of the text</param>
        /// <returns>Text return</returns>
        public string ToString(string format)
        {
            if (this == Complex.Zero)
            {
                return "0";
            }
            else if (double.IsInfinity(this.realpart) || double.IsInfinity(this.imaginaryPart))
            {
                return "oo";
            }
            else if (double.IsNaN(this.realpart) || double.IsNaN(this.imaginaryPart))
            {
                return "?";
            }

            string re, im, sign;

            string imval = Math.Abs(this.imaginaryPart).ToString(format);
            string reval = this.realpart.ToString(format);

            if (imval.StartsWith("-"))
            {
                if (reval == "0")
                {
                    sign = "-";
                }
                else
                {
                    sign = " - ";
                }
            }
            else if (imval != "0" && reval != "0")
            {
                sign = " + ";
            }
            else
            {
                sign = string.Empty;
            }

            if (imval == "0")
            {
                im = string.Empty;
            }
            else if (imval == "1")
            {
                im = "i";
            }
            else
            {
                im = imval + "i";
            }

            if (reval == "0")
            {
                if (imval != "0")
                {
                    re = string.Empty;
                }
                else
                {
                    re = "0";
                }
            }
            else
            {
                re = reval;
            }

            return re + sign + im;
        }

        /// <summary>
        /// Evaluate Equals
        /// </summary>
        /// <param name="obj">Object data</param>
        /// <returns>False or true</returns>
        public override bool Equals(object obj)
        {
            return obj.ToString() == this.ToString();
        }

        /// <summary>
        /// GetHashCode of class
        /// </summary>
        /// <returns>integer value</returns>
        public override int GetHashCode()
        {
            return -1;
        }

        #endregion

        #region Dynamics

        /// <summary>
        /// Is real the number
        /// </summary>
        /// <returns>True or false</returns>
        public bool IsReal()
        {
            return this.imaginaryPart == 0;
        }

        /// <summary>
        /// Is imaginary the number
        /// </summary>
        /// <returns>True or false</returns>
        public bool IsImaginary()
        {
            return this.realpart == 0;
        }

        #endregion
    }
}
